﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

namespace UFrame.ECS
{
    /// <summary>
    /// 一个实体
    /// 1. 全部的MonoBehavior按顺序初始化USystemMono
    /// 2. 只是让全部的MonoBehavior 放入usList集合
    /// USystemMono只能出现一个, 不支持多个
    /// </summary>
    [System.Serializable]
    public class UEntity
    {
        private List<UComponent> components = new List<UComponent>();
        private List<USystemMono> monoList = new List<USystemMono>();
        private List<USystem> systemList = new List<USystem>();
        private List<IUSystem> usList = new List<IUSystem>(); //存储当前全部的Monobehavor
        private GameObject root;
        public bool isQueueInitMa = false;
        private float initTime = 0f;
        private float initTimer = 6f; //4秒内必须初始化完毕
        public event Action onComplete;

        public GameObject Root
        {
            get { return root; }
            set { root = value; }
        }

        #region 组件

        public void SetRoot(GameObject go, bool isSearchSystem = true)
        {
            root = go;

            if (isSearchSystem)
            {
                //需要定义Mono的初始化顺序
                var order = go.GetComponent<UMonoInitOrder>();
                if (order != null)
                {
                    monoList.AddRange(order.orderList);
                }
                else
                {
                    //预制体不需要明确的生命周期,System初始化顺序无所谓
                    var moList = go.GetComponentsInChildren<USystemMono>(true);
                    monoList.AddRange(moList);
                }
            }
        }

        public void AddSystemRang(IEnumerable<USystemMono> monos)
        {
            monoList.AddRange(monos);
        }

        public T GetComponent<T>() where T : UComponent, new()
        {
            foreach (var item in components)
            {
                if (item is T)
                {
                    return (T) item;
                }
            }

            return default(T);
        }

        public bool IsComponent<T>() where T : UComponent, new()
        {
            foreach (var item in components)
            {
                if (item is T)
                {
                    return true;
                }
            }

            return false;
        }


        public T GetUSystem<T>() where T : USystem
        {
            foreach (var item in systemList)
            {
                if (item is T)
                {
                    return item as T;
                }
            }

            return null;
        }

        public T GetUSystemMono<T>() where T : MonoBehaviour
        {
            foreach (var item in monoList)
            {
                if (item is T)
                {
                    return item as T;
                }
            }

            foreach (var item in usList)
            {
                if (item is T)
                {
                    return item as T;
                }
            }

            return null;
        }

        public T AddComponent<T>() where T : UComponent, new()
        {
            T t = new T();
            components.Add(t);
            return t;
        }

        public void AddComponent<T>(T t) where T : UComponent
        {
            components.Add(t);
        }

        public USystem AddUSystem<T>() where T : USystem, new()
        {
            T t = new T();
            systemList.Add(new T());
            return t;
        }

        public void AddUSystem<T>(T t) where T : USystem, new()
        {
            systemList.Add(t);
        }

        #endregion

        public virtual void PrepareInit()
        {
            StartInit();
        }

        protected virtual void OnInitComplete()
        {
            //被复写的回调
        }

        private void StartInit()
        {
            CoroutineTool.I.StartCoroutine(CheckInit(monoList));
        }

        protected IEnumerator CheckInit(List<USystemMono> list)
        {
            //给组件统一设置Entity
            for (int i = 0; i < monoList.Count; i++)
            {
                monoList[i].entity = this;
                var t = monoList[i].GetType();
                var field = t.GetField("uEntity", BindingFlags.Instance | BindingFlags.NonPublic);
                if (field != null)
                    field.SetValue(monoList[i], this);
            }

            float waitTime = 0.01f;
            while (true)
            {
                yield return new WaitForSeconds(waitTime);

                if (isQueueInitMa)
                {
                    for (int i = 0; i < list.Count; i++)
                    {
                        IMa ma = list[i];
                        initTime = 0f;
                        if (!ma.IsInit)
                        {
                            ma.Init();
                        }

                        while (true)
                        {
                            float t = 0.001f;
                            yield return new WaitForSeconds(t);

                            if (ma.IsInit)
                            {
                                break;
                            }

                            initTime += t;
                            if (initTime >= initTimer)
                            {
                                PLDebug.LogError("not init Ma :" + ma.IsInit);
                                break;
                            }
                        }
                    }
                }
                else
                {
                    bool result = true;
                    for (int i = 0; i < list.Count; i++)
                    {
                        var systemMono = list[i];
                        if (!systemMono.isRunInit)
                        {
                            systemMono.Init();
                            systemMono.isRunInit = true;
                        }
                        
                        if (systemMono.IsInit == false)
                        {
                            result = false;
                            break;
                        }
                    }

                    if (result)
                    {
                        break;
                    }

                    initTime += waitTime;
                    if (initTime >= initTimer)
                    {
                        initTime = 0;
                        for (int i = 0; i < list.Count; i++)
                        {
                            if (!list[i].IsInit)
                            {
                                PLDebug.LogError("not init Ma :" + list[i].IsInit);
                            }
                        }

                        break;
                    }
                }
            }

            onComplete?.Invoke();
            OnInitComplete();
            for (int i = 0; i < monoList.Count; i++)
            {
                monoList[i].EntityCompleteInit();
            }
        }

        public void Release()
        {
            for (int i = 0; i < monoList.Count; i++)
            {
                monoList[i].Release();
            }

            for (int i = 0; i < systemList.Count; i++)
            {
                systemList[i].Release();
            }
        }
    }
}